class Hash

  # Replaces the hash without the given keys.
  def except!(*keys)
    tap { keys.each { |key| delete(key) } }
  end unless method_defined? :except!

  # Returns a hash that includes everything but the given keys.
  def except(*items)
    dup.except!(*items)
  end unless method_defined? :except

  # Similar to deep_symbolize_keys but modifies self
  def deep_symbolize_keys!
    tap do
      keys.each do |key|
        val = self.delete key
        self[(key.to_sym rescue key)] = case val
          when Hash
            val.deep_symbolize_keys!
          when Array
            val.map do |item|
              item.is_a?(Hash) ? item.deep_symbolize_keys! : item
            end
          else
            val
          end
      end
    end
  end unless method_defined? :deep_symbolize_keys!

  # Returns a new hash with all keys converted to symbols, as long as they respond to to_sym.
  # This includes the keys from the root hash and from all nested hashes and arrays.
  def deep_symbolize_keys
    dup.deep_symbolize_keys!
  end unless method_defined? :deep_symbolize_keys

  # Similar to deep_stringify_keys but modifies self
  def deep_stringify_keys!
    tap do
      keys.each do |key|
        val = delete key
        self[key.to_s] = case val
          when Hash
            val.deep_stringify_keys!
          when Array
            val.map do |item|
              item.is_a?(Hash) ? item.deep_stringify_keys! : item
            end
          else
            val
          end
      end
    end
  end unless method_defined? :deep_stringify_keys!

  # Returns a new hash with all keys converted to strings.
  # This includes the keys from the root hash and from all nested hashes and arrays.
  def deep_stringify_keys
    dup.deep_stringify_keys!
  end unless method_defined? :deep_stringify_keys

  # Similar to symbolize_keys but modifies self
  def symbolize_keys!
    tap do
      keys.each do |key|
        self[(key.to_sym rescue key)] = delete key
      end
    end
  end unless method_defined? :symbolize_keys!

  # Returns a new hash with all the keys converted to symbols
  def symbolize_keys
    dup.symbolize_keys!
  end unless method_defined? :symbolize_keys

  # Similar to stringify_keys but modifies self
  def stringify_keys!
    tap { keys.each { |key| self[key.to_s] = delete key } }
  end unless method_defined? :stringify_keys!

  # Returns a new hash with all the keys converted to string
  def stringify_keys
    dup.stringify_keys!
  end unless method_defined? :stringify_keys

  # Return all deeply nested keys
  def deep_keys
    all_keys = self.keys
    keys.each do |key|
      if self[key].is_a?(Hash)
        all_keys << self[key].deep_keys.compact.flatten
        next
      end
    end
    all_keys.flatten
  end unless method_defined? :deep_keys

  # Returns true if the given key is present inside deeply nested hash
  def deep_key?(key)
    deep_keys.include? key
  end unless method_defined? :deep_key?

  # Similar as deep_merge but modifies self
  def deep_merge!(other, &block)
    tap do
      other.each_pair do |key, other_val|
        this_val = self[key]
        self[key] = if this_val.is_a?(Hash) && other_val.is_a?(Hash)
          this_val.deep_merge! other_val, &block
        else
          block_given? && this_val ? block.call(key, this_val, other_val) : other_val
        end
      end
    end
  end unless method_defined? :deep_merge!

  # Returns a new hash with self and other_hash merged recursively.
  def deep_merge(other, &block)
    dup.deep_merge! other, &block
  end unless method_defined? :deep_merge

  # Replaces the hash with only the given keys.
  # Returns a hash containing the removed key/value pairs.
  def slice!(*keys)
    tap { keys.each { |key| delete(key) } }
  end unless method_defined? :slice!

  # Slice a hash to include only the given keys.
  # Returns a hash containing the given keys.
  def slice(*keys)
    keys.inject({}) { |hash, key| hash[key] = self[key] ; hash }
  end unless method_defined? :slice

end # Hash
